//	TeleoperationBoard.java --- 

package fr.upmc.dtgui.example.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;

import javax.swing.BorderFactory;
import javax.swing.BoundedRangeModel;
import javax.swing.BoxLayout;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import fr.upmc.dtgui.example.robot.EnergyData;
import fr.upmc.dtgui.example.robot.LittleRobot;
import fr.upmc.dtgui.example.robot.RobotActuatorCommand;
import fr.upmc.dtgui.example.robot.SpeedChange;
import fr.upmc.dtgui.example.robot.SpeedData;
import fr.upmc.dtgui.example.robot.SteeringChange;
import fr.upmc.dtgui.example.robot.SteeringData;

/**
 * The class <code>TeleoperationBoard</code> implements a teleoperation panel
 * for a little robot.  It is separated into three areas: an energy level
 * display, a speed control display and a steering control display.  The two
 * control panel are themselves composed of two subpanels, one for displaying
 * the current value and the other to allow for changing this value.  For
 * readability reasons (Swing does not provide good widgets to show a value
 * with a clear scale besides a slider bar), both of these panels use slider
 * bars.  In the subpanel displaying the current value, actions on the slider
 * bar are not activated.
 *
 * <p><strong>Invariant</strong></p>
 * 
 * <pre>
 * invariant	true
 * </pre>
 * 
 * <p>Created on : 2011-09-19</p>
 * 
 * @author	<a href="mailto:Jacques.Malenfant@lip6.fr">Jacques Malenfant</a>
 * @version	$Name$ -- $Revision$ -- $Date$
 */
public class TeleoperationBoard extends JPanel {

	private static final long serialVersionUID = 1L ;
	protected ArrayList<EnergyPanel>		ecv ;
	protected ArrayList<SpeedPanel>		sp ;
	protected ArrayList<SteeringPanel>		stp ;
	protected ArrayList<LittleRobot>		robots ;

	public			TeleoperationBoard(ArrayList<LittleRobot> robots) {
		super() ;
		this.robots = robots ;
		this.setSize(1000, 250) ;
		this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2)) ;
		
		int nbRobots = robots.size();
		//this.ecv = new EnergyPanel() ;
		this.ecv = new ArrayList<EnergyPanel>();
		for(int i=0; i<nbRobots; i++)
			this.ecv.add(new EnergyPanel(i));
		for(EnergyPanel ep : this.ecv)
			this.add(ep) ;
		
		int ind = 0;
		//this.sp = new SpeedPanel(lr) ;
		this.sp = new ArrayList<SpeedPanel>();
		for(LittleRobot lr : this.robots)
			sp.add(new SpeedPanel(lr,ind++));
		for(SpeedPanel spa : sp)
			this.add(spa) ;
		ind = 0;
		//this.stp = new SteeringPanel(lr) ;
		this.stp = new ArrayList<SteeringPanel>();
		for(LittleRobot lr : this.robots)
			this.stp.add(new SteeringPanel(lr, ind++));
		for(SteeringPanel ste : stp)
			this.add(ste) ;
	}

	@Override
	protected void	paintComponent(Graphics g) {
		super.paintComponent(g) ;
		this.setSize(1000, 250) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2)) ;
	}

	public void		updateEnergy(EnergyData ed, int id) {
		this.ecv.get(id).updateEnergy(ed) ;
	}

	public void		updateSpeed(SpeedData sd, int id) {
		this.sp.get(id).updateSpeed(sd) ;
	}

	public void		updateSteeringAngle(SteeringData sd, int id) {
		this.stp.get(id).updateSteeringAngle(sd) ;
	}
}

class EnergyPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected BoundedRangeModel energyModel ;
	
	public EnergyPanel(int ind) {
		this.setSize(100, 250) ;
		this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)) ;
		// JSlider to get nice labels
		this.energyModel = new DefaultBoundedRangeModel(0, 0, 0, 100) ;
		JSlider energySlider = new JSlider(energyModel) ;
		energySlider.setOrientation(JSlider.VERTICAL) ;
	    energySlider.setMajorTickSpacing(20);
	    energySlider.setMinorTickSpacing(5);
	    energySlider.setPaintTicks(true);
	    energySlider.setPaintLabels(true);
	    JPanel jpEnergySlider = new JPanel() ;
	    jpEnergySlider.add(energySlider) ;
	    this.add(jpEnergySlider) ;

		// The label that says what information is diplayed
		JLabel ecvLabel = new JLabel("Remaining energy") ;
		JPanel jpEcvLabel = new JPanel() ;
		jpEcvLabel.setLayout(new BorderLayout()) ;
		jpEcvLabel.add(ecvLabel, BorderLayout.NORTH) ;
		this.add(jpEcvLabel) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4)) ;
	}

	public void updateEnergy(EnergyData ed) {
		this.energyModel.setValue((int) Math.round(ed.level)) ;
	}
}

class SpeedPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected SpeedDisplayPanel		sdp ;
	protected SpeedControllerPanel	scp ;
	protected LittleRobot			lr ;
	protected int					ind;

	public			SpeedPanel(LittleRobot lr, int ind) {
		this.ind = ind;
		this.lr = lr ;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 250) ;
		this.sdp = new SpeedDisplayPanel(ind) ;
		this.scp = new SpeedControllerPanel(lr, ind) ;
		this.add(sdp, BorderLayout.NORTH) ;
		this.add(scp, BorderLayout.SOUTH) ;
	}

	public void updateSpeed(SpeedData sd) {
		this.sdp.updateSpeed(sd) ;
	}
}

class SpeedDisplayPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected BoundedRangeModel speedModel ;
	protected int ind;

	public			SpeedDisplayPanel(int ind) {
		this.ind = ind;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 125) ;
		JPanel jpProgressBar = new JPanel() ;
		jpProgressBar.setLayout(new FlowLayout()) ;
		this.speedModel = new DefaultBoundedRangeModel(0, 0, 0, 20) ;
		JSlider speedSlider = new JSlider(speedModel) ;
	    speedSlider.setMajorTickSpacing(5);
	    speedSlider.setMinorTickSpacing(1);
	    speedSlider.setPaintTicks(true);
	    speedSlider.setPaintLabels(true);
	    jpProgressBar.add(speedSlider) ;
		this.add(jpProgressBar, BorderLayout.NORTH) ;
		JLabel speedLabel = new JLabel("Current speed (m/s)") ;
		JPanel speedLabelPanel = new JPanel() ;
		speedLabelPanel.add(speedLabel) ;
		this.add(speedLabelPanel, BorderLayout.SOUTH) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4)) ;
	}

	public void updateSpeed(SpeedData sd) {
		this.speedModel.setValue((int) Math.round(sd.speed)) ;
	}
}

class SpeedControllerPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected LittleRobot		lr ;
	protected int				ind;

	public			SpeedControllerPanel(LittleRobot lr, int ind) {
		this.ind = ind;
		this.lr = lr ;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 125) ;
		JLabel speedLabel = new JLabel("Speed control (m/s)") ;
		JPanel speedLabelPanel = new JPanel() ;
		speedLabelPanel.add(speedLabel) ;
		this.add(speedLabelPanel, BorderLayout.SOUTH) ;
		DefaultBoundedRangeModel speedModel =
			new DefaultBoundedRangeModel(0, 0, 0, 20) ;
		JSlider speedSlider = new JSlider(speedModel) ;
	    speedSlider.setMajorTickSpacing(5);
	    speedSlider.setMinorTickSpacing(1);
	    speedSlider.setPaintTicks(true);
	    speedSlider.setPaintLabels(true);
	    speedSlider.addChangeListener(
	    	new SpeedActuatorDataListener(lr.getActuatorDataQueue())) ;
		JPanel speedSliderPanel = new JPanel() ;
		speedSliderPanel.add(speedSlider) ;
		this.add(speedSliderPanel, BorderLayout.NORTH) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4)) ;
	}

}

class SteeringPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected SteeringDisplayPanel		sdp ;
	protected SteeringControllerPanel	scp ;
	protected LittleRobot				lr ;
	protected int						ind ;

	public			SteeringPanel(LittleRobot lr, int ind) {
		this.ind = ind;
		this.lr = lr ;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 250) ;
		this.sdp = new SteeringDisplayPanel(ind) ;
		this.scp = new SteeringControllerPanel(lr, ind) ;
		this.add(sdp, BorderLayout.NORTH) ;
		this.add(scp, BorderLayout.SOUTH) ;
	}

	public void updateSteeringAngle(SteeringData sd) {
		this.sdp.updateSteeringAngle(sd) ;
 	}
}

class SteeringDisplayPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected BoundedRangeModel steeringModel ;
	protected int				ind;

	public			SteeringDisplayPanel(int ind) {
		this.ind = ind;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 125) ;
		JPanel jpProgressBar = new JPanel() ;
		jpProgressBar.setLayout(new FlowLayout()) ;
		this.steeringModel = new DefaultBoundedRangeModel(0, 0, -15, 15) ;
		JSlider steeringSlider = new JSlider(this.steeringModel) ;
		steeringSlider.setMajorTickSpacing(5);
		steeringSlider.setMinorTickSpacing(1);
		steeringSlider.setPaintTicks(true);
		steeringSlider.setPaintLabels(true);
	    jpProgressBar.add(steeringSlider) ;
		this.add(jpProgressBar, BorderLayout.NORTH) ;
		JLabel steeringLabel = new JLabel("Current steering angle (degrees)") ;
		JPanel speedLabelPanel = new JPanel() ;
		speedLabelPanel.add(steeringLabel) ;
		this.add(speedLabelPanel, BorderLayout.SOUTH) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4)) ;
	}

	public void updateSteeringAngle(SteeringData sd) {
		this.steeringModel.setValue((int) Math.round(sd.steeringAngle)) ;
	}

}

class SteeringControllerPanel extends JPanel {

	private static final long serialVersionUID = 1L;
	protected LittleRobot		lr ;
	protected int				ind;

	public			SteeringControllerPanel(LittleRobot lr, int ind) {
		this.ind = ind;
		this.lr = lr ;
		this.setLayout(new BorderLayout()) ;
		this.setSize(450, 125) ;
		JLabel steeringLabel = new JLabel("Steering angle control (degrees)") ;
		JPanel steeringLabelPanel = new JPanel() ;
		steeringLabelPanel.add(steeringLabel) ;
		this.add(steeringLabelPanel, BorderLayout.SOUTH) ;
		DefaultBoundedRangeModel steeringModel =
			new DefaultBoundedRangeModel(0, 0, -15, 15) ;
		JSlider steeringSlider = new JSlider(steeringModel) ;
		steeringSlider.setMajorTickSpacing(5);
		steeringSlider.setMinorTickSpacing(1);
		steeringSlider.setPaintTicks(true);
		steeringSlider.setPaintLabels(true);
		steeringSlider.addChangeListener(
	    	new SteeringActuatorDataListener(lr.getActuatorDataQueue())) ;
		JPanel steeringSliderPanel = new JPanel() ;
		steeringSliderPanel.add(steeringSlider) ;
		this.add(steeringSliderPanel, BorderLayout.NORTH) ;
		this.setBorder(BorderFactory.createLineBorder(Color.BLACK, 4)) ;
	}

}

class SpeedActuatorDataListener implements ChangeListener {

	final protected BlockingQueue<RobotActuatorCommand> commandQueue ;

	public SpeedActuatorDataListener(
		BlockingQueue<RobotActuatorCommand> commandQueue
		)
	{
		super();
		this.commandQueue = commandQueue;
	}

	@Override
	public void stateChanged(ChangeEvent e) {
		JSlider source = (JSlider)e.getSource() ;
		int newSpeed = source.getValue() ;
		final SpeedChange sc = new SpeedChange(newSpeed) ;
		(new ActuatorDataSender(sc, this.commandQueue)).start() ;
	}
	
}

class SteeringActuatorDataListener implements ChangeListener {

	final protected BlockingQueue<RobotActuatorCommand> commandQueue ;

	public SteeringActuatorDataListener(
			BlockingQueue<RobotActuatorCommand> commandQueue) {
		super();
		this.commandQueue = commandQueue;
	}

	@Override
	public void stateChanged(ChangeEvent e) {
		JSlider source = (JSlider)e.getSource() ;
		double newSteeringAngle = source.getValue() ;
		final SteeringChange sc = new SteeringChange(newSteeringAngle) ;
		(new ActuatorDataSender(sc, this.commandQueue)).start() ;
	}
	
}

// $Id$